import org.junit.Test;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 1.内存层面需要针对于多个数据进行存储.此时,可以考虑的容器有:数组,集合类
 * <p>
 * 2.数组存储多个数据方面的特点:
 * > 数组一旦初始化,其长度就是确定的.
 * > 数组中的多个元素是依次紧密排列的,有序的,可重复的.
 * > (优点) 数组一旦初始化完成,其元素的类型就确定了. 不是此类型的元素,就不能添加到此数组中.
 * > (优点) 元素的类型既可以是基本数据类型,也可以是引用数据类型.
 * 数组存储多个数据方面的弊端:
 * > 数组一旦初始化,其长度就不可变了.
 * > 数组中存储数据特点的单一性.对于无序的,不可重复的场景的多个数据就不能为力了.
 * > 数组中可用的方法/属性极少.具体的要求,都需要自己来组织相关的代码逻辑.
 * > 针对于数组中元素的删除/插入操作,性能较差.
 * <p>
 * 3.Java集合框架体系(Java.util包下)
 * java.util.Collection:存储一个一个的数据
 * --|子接口:List: 存储有序的,可重复的数据("动态"数组)
 * -----|---ArrayList(主要实现类):线程不安全的、效率高；底层使用Object[]数组
 * -----------------------------在添加数据、查找数据时，效率较高；在插入、删除数据时，效率较低。
 * -----|---LinkedList：底层使用双向链表的方式进行存储；在对集合中的数据进行删除、插入操作时，建议使用此类。
 * -----------------------------在添加数据、查找数据时，效率较低；在插入、删除数据时，效率较高。
 * -----|---Vector：List的古老实现类：线程安全的、效率低；底层使用Object[]数组存储
 * --|子接口:Set:存储无序的/不可重复的数据(集合)
 * -----|---HashSet(主要实现类)：底层使用的是HashMap，即使用数组+单向链表+红黑树结构进行存储。（jdk8）
 * ----------------|--LinkedHashSet:HashSet的子类：在现有数组+单向链表+红黑树结构的基础上，又添加了一组双向链表，用于记录添加元素的添加顺序。
 * -----|---TreeSet:底层使用红黑树存储。可以按照添加元素的指定的属性的大小顺序进行排列。
 * java.util.Map:存储一对一对的数据(key-value键值对)
 * --|--HashMap(主要实现类)：线程不安全的；可以添加null的key或value值;底层使用数组+单向链表+红黑树结构存储（jdk8）
 * ----------|--LinkedHashMap：是HashMap的子类，在父类基使用的数据结构基础上，增加了一对双向链表，用于记录添加元素的先后顺序，遍历时，按添加顺序
 * --|--TreeMap：底层使用红黑树存储。
 * --|--Hashtable：古老实现类；线程安全的；不可以添加null的key或value值；底层使用数组+单向链表结构存储（jdk8）
 * ----------|--Properties:Hashtable的子类；其key和value都是String类型。常用来处理属性文件。
 * <p>
 * Collection
 */
public class CollectionTest {
    @Test
    public void test() {
        ArrayList<Object> arrayList = new ArrayList<>();
        arrayList.add(123);
        System.out.println(arrayList.get(0).getClass());
        System.out.println(arrayList.add(123));
        System.out.println(arrayList.size());
        System.out.println(arrayList);

        Person person = new Person("Tom", 13);
        arrayList.add(person);
        Object o = new Person("Tom", 13);
        System.out.println(arrayList.contains(o));
    }

    @Test
    public void test2() {
        String[] arr = new String[]{"aa", "bb"};
        List<String> list = Arrays.asList(arr);
    }

    @Test
    public void test3() {
        Integer[] arr = new Integer[]{1, 2, 3};
        List<Integer> list = Arrays.asList(arr);
        System.out.println(list.size()); // 3

        int[] arr1 = new int[]{1, 2, 3};
        List<Integer> list1 = Arrays.asList(arr);
        System.out.println(list1.size());// 1
    }

    /**
     * 迭代器
     * 遍历集合元素
     * <p>
     * > 针对集合来讲，增强for循环的底层仍然使用的是迭代器。
     * > 增强for循环的执行过程中，是将集合或数组中的元素依次赋值给临时变量。
     */
    @Test
    public void testIterator() {
        Integer[] arr = new Integer[]{1, 2, 3};
        // Arrays.asList()
        // 返回的是 固定大小列表（基于原始数组的视图），不支持结构性修改操作（如 add/remove）
        List<Integer> list = Arrays.asList(arr);

        // 获取迭代器的对象
        Iterator<Integer> iterator = list.iterator();
        System.out.println(iterator.getClass());

        while (iterator.hasNext()) {
            // next()
            // ① 指针下移
            // ② 将下移以后集合位置上的元素返回
            System.out.println(iterator.next());
        }
        System.out.println(list.indexOf(2));
        // 这段代码会抛出 UnsupportedOperationException 异常
//         list.remove(2);
        System.out.println(list);
    }

    @Test
    public void testAsList() {
        Integer[] arr = new Integer[]{1, 2, 3};
        // 方案1：创建真正的 ArrayList
        List<Integer> list = new ArrayList<>(Arrays.asList(arr));
        list.remove(2); // 正常执行
        List<Integer> l = Arrays.asList(arr);
//        l.remove(2); // UnsupportedOperationException
        Collections.swap(list, 1, 0); // 可交换
        System.out.println(list);

// 方案2：使用 Java 8+ 的流（推荐）
        List<Integer> list1 =
                Arrays.stream(arr).collect(Collectors.toList());
        list1.remove(2);
        System.out.println(list1);

        /*
        Arrays.asList() 返回的列表特性：
        固定长度（长度与原始数组一致）
        修改元素值会影响原始数组（因为底层共享数组）
        不支持任何改变长度的操作（add/remove）
        Java 集合框架中不可变列表的常见实现：
        Collections.emptyList()
        List.of()（Java 9+）
        Arrays.asList()
         */

    }

    @Test
    public void test4() {
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("2");
        list.add("c");

        list.remove(2);
        System.out.println(list);
    }

    /**
     * --|子接口:Set:存储无序的/不可重复的数据(集合)
     * <p>
     * 无序性!=随机性
     * <p>
     * 不可重复性
     */
    @Test
    public void testSet() {
        Set<Object> set = new HashSet<>();
        set.add("a");
        set.add("\"");
        set.add("b");
        set.add(123);
        set.add(1234);
        set.add("AA");
        set.add("BB");
        for (Object o :
                set) {
            System.out.println(o);
        }

        System.out.println(set.contains(new Person("Tom", 12)));
    }

    @Test
    public void testLinkedSet() {
        LinkedHashSet<Object> set = new LinkedHashSet<>();
        set.add("a");
        set.add("\"");
        set.add("v");
        set.add(123);

        System.out.println(set);
    }

    /**
     * TreeSet
     * <p>
     * 要求添加到TreeSet中的元素必须是同一个类型的对象，
     * 否则会报ClassCastException
     * <p>
     * 判断数据是否相等的标准
     * > 不再考虑hashCode()和equals()方法了，
     * 也就意味着添加到TreeSet中的元素所在的类不需要重写hashCode()和equals()方法
     * > 比较元素大小的或比较元素是否相等的标准就是考虑自然排序或定制排序中，
     * compareTo()/compare()的返回值。
     */
    @Test
    public void testTreeSet() {
        TreeSet<Object> set = new TreeSet<>();
        // 自然排序
        set.add("a");
        set.add("\"");
        set.add("v");
        set.add("123");
        // set.add(123);// ClassCastException
        System.out.println(set);
        // 自然排序
        TreeSet<Person> personTreeSet = new TreeSet<>();
        personTreeSet.add(new Person("Tom", 12));
        personTreeSet.add(new Person("Jerry", 11));
        personTreeSet.add(new Person("Marry", 14));
        personTreeSet.add(new Person("Piggy", 15));
        personTreeSet.add(new Person("George", 12));
        personTreeSet.add(new Person("Cap", 120));
        for (Person p :
                personTreeSet) {
            System.out.println(p);
        }

        // 定制排序
        System.out.println("定制排序");
        Comparator<Person> comparator = new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o2.compareTo(o1);
            }
        };
        TreeSet<Person> treeSet = new TreeSet<>(comparator);
        treeSet.add(new Person("Tom", 12));
        treeSet.add(new Person("Jerry", 11));
        treeSet.add(new Person("Marry", 14));
        treeSet.add(new Person("Piggy", 15));
        treeSet.add(new Person("George", 12));
        treeSet.add(new Person("Cap", 120));
        for (Person p :
                treeSet) {
            System.out.println(p);
        }
    }

    @Test
    public void testDuplicateList() {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(15);
        list.add(1567);
        list.add(13);
        list.add(12);
        list.add(12);
        list.add(12);
        list.add(2);
        System.out.println(duplicateList(list));

    }

    /**
     * 参数List中只存放Integer对象
     * 去重
     *
     * @param list 输入list
     * @return 输出list
     */
    public static List<Integer> duplicateList(List<Integer> list) {
        HashSet<Integer> integers = new HashSet<>(list);
        return integers.stream().toList();
    }

    /**
     * 获取10个 1-20 的随机数，要求随机数不能重复，并把最终的随机数输出到控制台。
     */
    @Test
    public void testRandom() {
        Random random = new Random();
        HashSet<Integer> randomNum = new HashSet<>();
        int i = 0;
        while (randomNum.size() < 10) {
            randomNum.add(random.nextInt(20) + 1);
            i++;
        }

        System.out.println(randomNum);
        System.out.println(i);
    }

    /**
     * Map中key用Set来存放，不允许重复，即同一个Map对象所对应的类，必须重写hashCode()和equals()方法
     * > HashMap中的所有的key彼此间是不可重复的、无序的。所有的key就构成一个Set集合。
     * > HashMap中的所有的value彼此之间是可重复的、无序的。所有的value就构成一个Collection集合。--->value所在类要重写equals()
     * > HashMap中的一个key-value就构成一个entry。
     * > HashMap中的所有的entry彼此之间是不可重复的、无序的。所有的entry就构成一个Set集合。
     */

    @Test
    public void testMap() {
        Map<Object, Object> map = new HashMap<>();
        map.put(null, null);
        map.put("AA", 11);
        System.out.println(map);
        System.out.println(map.values());
        System.out.println(map.keySet());


        for (Map.Entry<Object, Object> entry : map.entrySet()) {
            System.out.println(entry);
        }
//        Map map = new Hashtable();
//        map.put(null, null); // NullPointerException
//        System.out.println(map);
    }

    /**
     *
     */
    @Test
    public void testTreeMap() {
        TreeMap<Object, Object> treeMap = new TreeMap<>();
        treeMap.put("CC", 88);
        treeMap.put("BC", 98);
        treeMap.put("AC", 68);
        treeMap.put("DDC", 79);
        treeMap.put("EE", 71);
        for (Map.Entry<Object, Object> entry : treeMap.entrySet()) {
            System.out.println(entry);
        }

        // 自然排序
        TreeMap<Person, Integer> personTreeSet = new TreeMap<>();
        personTreeSet.put(new Person("Tom", 12), 12);
        personTreeSet.put(new Person("Jerry", 11), 11);
        personTreeSet.put(new Person("Marry", 14), 14);
        personTreeSet.put(new Person("Piggy", 15), 15);
        personTreeSet.put(new Person("George", 12), 12);
        personTreeSet.put(new Person("Cap", 120), 120);
        System.out.println(personTreeSet);
    }

    @Test
    public void testProperty() {
        File file = new File("info.properties");
        Properties properties = new Properties();
        try (FileInputStream fis = new FileInputStream(file)) {
            properties.load(fis);// 加载流中的文件中的数据

            String name = properties.getProperty("username");
            String password = properties.getProperty("password");
            System.out.println(name);
            System.out.println(password);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}
